#ifndef QP_QSSERT_HPP
#define QP_QSSERT_HPP

#ifdef Q_NASSERT /* Q_NASSERT defined--assertion checking disabled */

#define Q_DEFINE_THIS_FILE
#define Q_DEFINE_THIS_MODULE(name_)
#define Q_ASSERT(test_) ((void)0)
#define Q_ASSERT_ID(id_, test_) ((void)0)
#define Q_ALLEGE(test_) ((void)(test_))
#define Q_ALLEGE_ID(id_, test_) ((void)(test_))
#define Q_ERROR() ((void)0)
#define Q_ERROR_ID(id_) ((void)0)

#else /* Q_NASSERT not defined--assertion checking enabled */

/** Place this macro at the top of each C/C++ module to define the file
* name string using __FILE__ (NOTE: __FILE__ might contain lengthy path
* name). This file name will be used in reporting assertions in this file.
*/
#define Q_DEFINE_THIS_FILE static char const Q_this_module_[] = __FILE__;

/** Place this macro at the top of each C/C++ module to define the module
* name as the argument \a name_. This file name will be used in reporting
* assertions in this file.
*/
#define Q_DEFINE_THIS_MODULE(name_) static char const Q_this_module_[] = name_;

/** General purpose assertion that makes sure the \a test_ argument is
* TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates
* to FALSE.
* \note the \a test_ is NOT evaluated if assertions are disabled with
* the Q_NASSERT switch.
* \sa #Q_ASSERT_ID
*/
#define Q_ASSERT(test_) \
  ((test_) ? (void)0 : Q_onAssert(&Q_this_module_[0], (int)__LINE__))

/** General purpose assertion that makes sure the \a test_ argument is
* TRUE. Calls the Q_onAssert() callback if the \a test_ evaluates
* to FALSE. The argument \a id_ is the ID number (unique within
* the file) of the assertion. This assertion style is better suited
* for unit testig, because it avoids the volatility of line numbers
* for identifying assertions.
* \note the \a test_ is NOT evaluated if assertions are disabled with
* the Q_NASSERT switch.
* \sa #Q_ASSERT
*/
#define Q_ASSERT_ID(id_, test_) \
  ((test_) ? (void)0 : Q_onAssert(&Q_this_module_[0], (int)(id_)))

/** General purpose assertion that ALWAYS evaluates the \a test_
* argument and calls the Q_onAssert() callback if the \a test_
* evaluates to FALSE.
* \note the \a test_ argument IS always evaluated even when assertions
* are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is
* defined, the Q_onAssert() callback is NOT called, even if the
* \a test_ evaluates to FALSE.
* \sa #Q_ALLEGE_ID
*/
#define Q_ALLEGE(test_) Q_ASSERT(test_)

/** General purpose assertion that ALWAYS evaluates the \a test_
* argument and calls the Q_onAssert() callback if the \a test_
* evaluates to FALSE. This assertion style is better suited
* for unit testing, because it avoids the volatility of line numbers
* for identifying assertions.
* \note the \a test_ argument IS always evaluated even when assertions
* are disabled with the Q_NASSERT macro. When the Q_NASSERT macro is
* defined, the Q_onAssert() callback is NOT called, even if the
* \a test_ evaluates to FALSE.
* \sa #Q_ALLEGE
*/
#define Q_ALLEGE_ID(id_, test_) Q_ASSERT_ID(id_, test_)

/** Assertion that always calls the Q_onAssert() callback if
* ever executed.
* \note can be disabled with the Q_NASSERT switch.
* \sa #Q_ERROR_ID
*/
#define Q_ERROR() Q_onAssert(&Q_this_module_[0], (int)__LINE__)

/** Assertion that always calls the Q_onAssert() callback if
* ever executed. This assertion style is better suited for unit
* testing, because it avoids the volatility of line numbers for
* identifying assertions.
* \note can be disabled with the Q_NASSERT switch.
* \sa #Q_ERROR
*/
#define Q_ERROR_ID(id_) Q_onAssert(&Q_this_module_[0], (int)(id_))

#endif /* Q_NASSERT */

#ifdef __cplusplus
extern "C" {
#endif

/** callback invoked in case the condition passed to #Q_ASSERT, #Q_REQUIRE,
* #Q_ENSURE, #Q_ERROR, #Q_ALLEGE as well as #Q_ASSERT_ID, #Q_REQUIRE_ID,
* #Q_ENSURE_ID, #Q_ERROR_ID, and #Q_ALLEGE_ID evaluates to FALSE.
*
* \param file file name where the assertion failed
* \param line line number at which the assertion failed
*/
void Q_onAssert(char const* const module, int location);

#ifdef __cplusplus
}
#endif

/** Assertion that checks for a precondition. This macro is equivalent to
* #Q_ASSERT, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_REQUIRE_ID
*/
#define Q_REQUIRE(test_) Q_ASSERT(test_)

/** Assertion that checks for a precondition. This macro is equivalent to
* #Q_ASSERT_ID, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_REQUIRE
*/
#define Q_REQUIRE_ID(id_, test_) Q_ASSERT_ID(id_, test_)

/** Assertion that checks for a postcondition. This macro is equivalent to
* #Q_ASSERT, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_ENSURE_ID
*/
#define Q_ENSURE(test_) Q_ASSERT(test_)

/** Assertion that checks for a postcondition. This macro is equivalent to
* #Q_ASSERT_ID, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_ENSURE
*/
#define Q_ENSURE_ID(id_, test_) Q_ASSERT_ID(id_, test_)

/** Assertion that checks for an invariant. This macro is equivalent to
* #Q_ASSERT, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_INVARIANT_ID
*/
#define Q_INVARIANT(test_) Q_ASSERT(test_)

/** Assertion that checks for an invariant. This macro is equivalent to
* #Q_ASSERT_ID, except the name provides a better documentation of the
* intention of this assertion.
* \sa #Q_INVARIANT
*/
#define Q_INVARIANT_ID(id_, test_) Q_ASSERT_ID(id_, test_)

#endif  // QP_QSSERT_HPP
